package com.sinai.mshab.client.ui.filter;

import java.util.ArrayList;

import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.client.util.FieldName;
import com.sinai.mshab.client.ui.FormComponent;
import com.sinai.mshab.client.ui.aggregate.FormData;
import com.sinai.mshab.client.ui.aggregate.HorizontalComponentList;
import com.sinai.mshab.client.ui.basic.DateQuestionComponent;
import com.sinai.mshab.client.ui.basic.NotifierButton;
import com.sinai.mshab.client.ui.basic.SuggestTextComponent;
import com.sinai.mshab.client.ui.basic.TextQuestionComponent;

/**
 * Creates a new filter object. This object will have 4 columns as follows:
 * 
 * field name | comparison | value |
 * 
 * The toString() of this class will return for example: "Patient.age >= 50"
 * 
 * which is constructed form the above columns. Many of these filters can be put
 * together to create and extensive WHERE clause.
 * 
 * @author serge
 * 
 */
public class Filter extends HorizontalComponentList implements BasicFilter {

	private ListBox columns, operator;
	private FormComponent value;
	private String table_name;
	private FilteredTable filtered_table;
	private FilterAggregate filtered_agg;
	private boolean replacement_loaded = false;
	private FormComponent replacement;
	
	public Filter(String table_name, FilteredTable t, FilterAggregate agg) {
		columns = new ListBox();
		operator = new ListBox();
		this.table_name = table_name;
		this.filtered_table = t;
		this.filtered_agg = agg;

		// ensure form data has the db structure
		FormData.getInstance().getDbStructure();

		// populating columns for the intial table.
		populate_columns();

		// populating operators
		setDefaultOperators();

		// adding all the components.
		Label col_label = new Label("Criteria: ");
		col_label.setStyleName("sub_description_label");

		value = createTextBox();

		// adding everything to the panel
		this.add(col_label);
		this.add(columns);
		this.add(operator);
		this.add(value);

		// adding a remove button
		this.addNotifyingComponent(new NotifierButton("Remove"));

		// adding a column listener which will substitute proper widget upon
		// column change types.
		columns.addChangeListener(new ColumnChangeListner());

		// implementing the highlighting.
		operator.addFocusListener(this);
		columns.addFocusListener(this);
		operator.setStyleName("drop_down");
		columns.setStyleName("drop_down");

		// setting value to the proper widget.
		setProperValueField();

		System.out.println("Filter populated itself with db data");
	}

	private void setDefaultOperators() {
		operator.clear();
		operator.addItem("contains");
		operator.addItem("does not contain");
		operator.addItem("is");
		operator.addItem("is not");
		operator.addItem(">");
		operator.addItem(">=");
		operator.addItem("<");
		operator.addItem("<=");
	}

	private void setDateOperators() {
		operator.clear();
		operator.addItem("before");
		operator.addItem("after");
		operator.addItem("before including");
		operator.addItem("after including");
		operator.addItem("on");
		operator.addItem("not on");
	}

	/**
	 * When to focus is lost validate the field.
	 */
	public void onFocus(Widget sender) {
		String name = ((ListBox) sender).getStyleName();
		((ListBox) sender).setStyleName(name + "_focus");
	}

	/**
	 * When to focus is lost validate the field.
	 */
	public void onLostFocus(Widget sender) {

		String name = ((ListBox) sender).getStyleName();
		if (name.indexOf("_focus") != -1) {
			((ListBox) sender).setStyleName(name.substring(0, name.length()
					- "_focus".length()));
		}
	}

	/**
	 * Creates a textbox to be used for value entry
	 * 
	 * @return the nicely formatted textbox for filter's use
	 */
	private SuggestTextComponent createTextBox() {
		SuggestTextComponent val = new SuggestTextComponent(getColumn(),
				getTableName(), "");
		val.setDescriptionStyleName("label_compact");
		val.setInnerStyleName("textbox_short");
		val.disableExpand();
		val.setWidth(110);
		return val;
	}

	/**
	 * Creates a textbox to be used for value entry
	 * 
	 * @return the nicely formatted textbox for filter's use
	 */
	private DateQuestionComponent createDateBox() {
		DateQuestionComponent val = new DateQuestionComponent("", "", "", "");
		val.setDescriptionStyleName("label_compact");
		val.setInnerStyleName("textbox_short");
		val.setWidth(90);
		return val;
	}

	/**
	 * Depending on the type of selected column, the value field will be either
	 * a textbox or a date picker.
	 */
	public void setProperValueField() {

		System.out.println("Attempting to set proper widget type.");

		String table = getTableName();
		String column = getColumn();
		String type = FormData.getColumnType(table, column);

		// see if there is a substitute filter.
		BasicFilter replacement = this.filtered_agg.filtered_table
				.getReplacementFilter(table, column);
		
		// if there is soemthing to replace the thing by.
		if (replacement != null) {
			this.replacement = (FormComponent)replacement;
			replacement_loaded = true;
			this.add((FormComponent)replacement, 2);
			value.setVisible(false);
			operator.setVisible(false);
		}
		// if it 's a text field, use textbox
		else if (!type.startsWith("date")) {
			
			// removing the replacement filter.
			if (replacement_loaded) {
				this.remove(this.replacement);
				replacement_loaded = false;
			}
			value.setVisible(true);
			operator.setVisible(true);
			setDefaultOperators();
			this.remove(value);
			value = createTextBox();
			this.add(value, 3);
			System.out.println("Created a textbox");
		}
		// if it's a date, use a date picker
		else if (type.startsWith("date")) {
			
			// removing the replacement filter.
			if (replacement_loaded) {
				this.remove(this.replacement);
				replacement_loaded = false;
			}
			value.setVisible(true);
			operator.setVisible(true);
			setDateOperators();
			this.remove(value);
			value = createDateBox();
			this.add(value, 3);
			System.out.println("Created a date");
		}
	}

	/**
	 * Fills in the columns drop down with the column names of the selected
	 * table name.
	 */
	public void populate_columns() {
		String table_name = getTableName();

		columns.clear();

		// populating the columns
		ArrayList column_names = FormData.getColumns(table_name);

		if (column_names == null) {
			System.out.println("Got null columns for " + table_name);
			return;
		}

		for (int i = 0; i < column_names.size(); i++) {
			FieldName field = (FieldName) column_names.get(i);
			String name = field.getName();
			if (!FormData.is_column_blacklisted(name)) {
				columns.addItem(name.replace('_', ' '));
			}
		}
		System.out.println("Filter populated itself with db data");
	}

	/**
	 * Returns the table associated with this filter.
	 * 
	 * @return the table associated with this filter
	 */
	public String getTableName() {
		return table_name;
	}

	/**
	 * Returns the column associated with this filter.
	 * 
	 * @return the column associated with this filter
	 */
	public String getColumn() {
		String col = columns.getItemText(columns.getSelectedIndex());
		col = col.trim();
		col = col.replace(' ', '_');
		col = col.replaceAll("'", "\\'");
		return col;
	}

	/**
	 * Returns the operator associated with this filter.
	 * 
	 * @return the operator associated with this filter
	 */
	public String getOperator() {
		
		// load the operator of the filter.
		if (replacement_loaded) {
			return ((BasicFilter)replacement).getOperator();
		}
		
		String op = operator.getItemText(operator.getSelectedIndex());
		String sql_op = op;

		// translating human readable gui stuff to sql operators
		if (op.equals("is")) {
			sql_op = "=";
		} else if (op.equals("is not") || op.equals("not on")) {
			sql_op = "<>";
		} else if (op.equals("contains")) {
			sql_op = "LIKE";
		} else if (op.equals("does not contain")) {
			sql_op = "NOT LIKE";
		} else if (op.equals("after")) {
			sql_op = ">";
		} else if (op.equals("before")) {
			sql_op = "<";
		} else if (op.equals("after including")) {
			sql_op = ">=";
		} else if (op.equals("before including")) {
			sql_op = "<=";
		} else if (op.equals("on")) {
			sql_op = "=";
		}

		return sql_op;
	}

	/**
	 * Returns the value associated with this filter.
	 * 
	 * @return the value associated with this filter
	 */
	public String getValue() {
		
		// load the value of the filter.
		if (replacement_loaded) {
			return ((BasicFilter)replacement).getValue();
		}
		
		String val = value.getAnswer().getFirstAnswer();
		val = val.replaceAll("'", "\\\\'");
		return val;
	}

	/**
	 * Tell filtered table to remove this filter.
	 */
	public void receiveNotification(FormComponent who) {
		filtered_agg.removeFilter(this);
	}

	private class ColumnChangeListner implements ChangeListener {

		public void onChange(Widget sender) {
			// tell filtered table to update it's sort by column list.
			filtered_table.updateColumns();

			setProperValueField();
		}
	}
}
